﻿#if WINDOWS || XBOX
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using SgMotion;
using SgMotion.Controllers;
using SynapseGaming.LightingSystem.Rendering;
using The_LarX.Rendering;

namespace The_LarX
{
    public class sgMotionAnimatedActor : Actor, I3DSgMotionObject
    {
        #region Variables
        /// <summary>
        /// The SkinnedModel class handles skeletal animated models
        /// </summary>
#if WINDOWS
        [Category("General")]
        [Description("The SkinnedModel class handles skeletal animated models")]
#endif
        public SkinnedModel SkinnedModel { get; set; }

        /// <summary>
        /// The AnimationController class handles the animation playback
        /// </summary>
#if WINDOWS
        [Category("General")]
        [Description("The AnimationController class handles the animation playback")]
#endif
        public AnimationController AnimationController { get; set; }

        public bool UpdateAnimationController = true;

        public override string ModelName
        {
            get { return modelName; }
            set
            {
                bool first;
                SetModelName(value, out first);
            }
        }

        
        public override void SetModelName(string modelName, out bool FirstTime)
        {
            this.modelName = modelName;

            SkinnedModel = Engine.Content.Load<SkinnedModel>(modelName, out FirstTime);
            Model = SkinnedModel.Model;

            AnimationController = new AnimationController(SkinnedModel.SkeletonBones);
            if (SkinnedModel.AnimationClips.ContainsKey("Idle"))
            {
                AnimationController.StartClip(SkinnedModel.AnimationClips["Idle"]);
            }
            else
            {
                if (SkinnedModel.AnimationClips.Count > 0)
                    AnimationController.StartClip(SkinnedModel.AnimationClips.First().Value);
            }

            if (SceneObject != null)
                if (RemoveActorFromRenderer != null)
                    RemoveActorFromRenderer(this);
            SceneObject = new SceneObject(this.Model);

            for (int i = 0; i < SceneObject.RenderableMeshes.Count; i++)
                SceneObject.RenderableMeshes[i].MeshToObject = Matrix.Identity;

            if (AddActorToRenderer != null)
                AddActorToRenderer(this);

            if (SubActors != null)
            {
                SubActors.Clear();
            }
            else
            {
                SubActors = new List<SubActor>();
            }

            for (int i = 0; i < SceneObject.RenderableMeshes.Count; i++)
            {
                SubActor actor = new SubActor(SceneObject, i);
                actor.Material = new Material(actor.Effect);
                SubActors.Add(actor);
            }

            foreach (var mesh in SceneObject.RenderableMeshes)
            {
                var bb = mesh.MeshBoundingBox;
                mesh.MeshBoundingBox = new BoundingBox(new Vector3(bb.Min.X, bb.Min.Z, -bb.Max.Y),
                                                      new Vector3(bb.Max.X, bb.Max.Z, -bb.Min.Y));
            }
            SceneObject.CalculateBounds();
        }

        public AnimationClipDictionary Animations
        {
            get
            {
                return SkinnedModel.AnimationClips;
            }
        }

        public string CurrentAnimation 
        {
            get
            {
                if (AnimationController.AnimationClip != null)
                {
                    return AnimationController.AnimationClip.Name;
                }
                else
                {
                    return "";
                }
            }
            set
            {
                if (SkinnedModel.AnimationClips.ContainsKey(value))
                {
                    if (Blend)
                    {
                        AnimationController.CrossFade(SkinnedModel.AnimationClips[value], TimeSpan.FromSeconds(BlendTime));
                    }
                    else
                    {
                        AnimationController.StartClip(SkinnedModel.AnimationClips[value]);
                    }
                }
                currentAnimation = value;
            }
        }
        public void FadeAnimation(AnimationClip clip)
        {
            AnimationController.CrossFade(clip, TimeSpan.FromSeconds(BlendTime));
        }
        protected string currentAnimation;

        private bool backward;
        public bool PlayBackward_XNAnimation
        {
            get { return backward; }
            set
            {
                if (value)
                    AnimationController.PlaybackMode = PlaybackMode.Backward;
                else
                    AnimationController.PlaybackMode = PlaybackMode.Forward;
            }
        }
        public bool Blend { get; set; }
        public float BlendTime { get; set; }

        #endregion

        public sgMotionAnimatedActor(string Place)
            : base()
        {
            Blend = true;
            BlendTime = 0.3f;
            this.ModelName = Place;
            this.AnimationController.LoopEnabled = true;
        }
        public sgMotionAnimatedActor(bool RemapIfFirst, string Place)
            : base()
        {
            Blend = true;
            BlendTime = 0.3f;
            this.ModelName = Place;
            this.AnimationController.LoopEnabled = true;
        }
        public sgMotionAnimatedActor()
            : base()
        {            
            Blend = true;
            BlendTime = 0.3f;
        }

        public Matrix TranslationOffsetMatrix = Matrix.Identity;
        public override void Update()
        {            
            if (UpdateAnimationController)
            {
                AnimationController.Update(Engine.GameTime.ElapsedGameTime, this.sizeMatrix * TranslationOffsetMatrix);
            }
            SceneObject.SkinBones = AnimationController.SkinnedBoneTransforms;
            base.Update();
        }
        public override void Draw()
        {
            bool VectorChanged = false;
            if (lastPosition != Position)
            {
                if (CalculateTranslationMatrix)
                {
                    transMatrix = Matrix.CreateTranslation(Position);
                    VectorChanged = true;
                }
                lastPosition = Position;
            }
            if (Scale != lastScale)
            {
                if (CalculateSizeMatrix)
                {
                    sizeMatrix = Matrix.CreateScale(Scale);
                    VectorChanged = true;
                }
                lastScale = Scale;
            }
            if (RadiansRotation != lastRadiansRotation)
            {
                if (CalculateRotationMatrix)
                {
                    rotationMatrix = MatrixUtil.CreateRotationMatrix(RadiansRotation);
                    VectorChanged = true;
                }
                lastRadiansRotation = RadiansRotation;
            }

            if (CalculateWorldMatrix)
            {
                if (VectorChanged)
                {
                    world = RotationMatrix * transMatrix;
                }
            }

            SceneObject.HullType = HullType.Sphere;
            SceneObject.World = world;
        }

        public override void SerializeData(SerializeData Data)
        {
            base.SerializeData(Data);

            Data.AddData("AnimatedActor.Blend", this.Blend);
            Data.AddData("AnimatedActor.BlendTime", this.BlendTime);
            Data.AddData("AnimatedActor.BackwardPlay", this.backward);
        }
        public override void DeserializeData(SerializeData Data)
        {
            base.DeserializeData(Data);

            this.Blend = Data.GetData<bool>("AnimatedActor.Blend");
            this.BlendTime = Data.GetData<float>("AnimatedActor.BlendTime");
            this.backward = Data.GetData<bool>("AnimatedActor.BackwardPlay");
        }

        public override ICloneable Clone()
        {
            sgMotionAnimatedActor actor = (sgMotionAnimatedActor)base.Clone();
            actor.BlendTime = this.BlendTime;
            return actor;
        }
    }
}
#endif
